/**
 * 
 */
package com.gragra.nounIdentification;
import it.unimi.dsi.fastutil.doubles.Double2ObjectRBTreeMap;
import it.unimi.dsi.fastutil.doubles.Double2ObjectSortedMap;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntCollection;
import it.unimi.dsi.fastutil.ints.IntIterator;
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.ints.IntSet;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import com.gragra.frameStatistics.StatisticsGenerator;
import com.gragra.util.StringToInt;
/**
 * This class implements the scoring and sorting of frames by the number of different co-occurrence word types
 * that are currently covered, divided by the total number of co-occurrences that the frame has;
 * it implements the scoring of the words by the total number of occurrences in a currently covered frame
 * divided by the total number of occurrences; the default score for iteration 0 in the frames is the
 * number of co-occurring word types; note that the same list is used for all operations, meaning that
 * the list that is returned will change if another method from some instance is called
 * @author Christoph Teichmann
 * created Mar 26, 2013 11:04:27 AM
 * @version 0.1
 */
public class TypesPerTokenScorer implements Scorer
{
	/**
	 * 
	 */
	private final IntArrayList frequent = new IntArrayList();
	/**
	 * 
	 * @param frames
	 * @param sg
	 */
	protected void sortFrames(IntCollection frames, StatisticsGenerator sg)
	{
		int topNUsed = (int) (((double) frames.size())/100.0) + 1;
		IntIterator iit = frames.iterator();
		this.sorter.clear();
		while(iit.hasNext())
		{
			int n = iit.next();
			double freq = sg.getFrameFrequency(n);
			if(!this.checkForPunct(sg, n))
			{this.putIntoMap(n,-freq);}
		}
		getFrequent().clear();
		ObjectIterator<IntArrayList> vals = sorter.values().iterator();
		while(vals.hasNext())
		{
			IntArrayList ial = vals.next();
			for(int i=0;i<ial.size();++i)
			{getFrequent().add(ial.get(i));}
			if(getFrequent().size() >= topNUsed)
			{return;}
		}
	}
	/* (non-Javadoc)
	 * @see com.gragra.nounIdentification.Scorer#sortWords(it.unimi.dsi.fastutil.ints.IntCollection, com.gragra.frameStatistics.StatisticsGenerator, it.unimi.dsi.fastutil.ints.IntSet)
	 */
	@Override
	public IntList sortWords(IntCollection words,
			StatisticsGenerator statisticsGenerator, IntSet restriction,int iteration)
	{
		sorter.clear();
		IntIterator iit = words.iterator();
		while(iit.hasNext())
		{
			int word = iit.nextInt();
			double freq = statisticsGenerator.getWordCount(word);
			IntIterator intIt = restriction.iterator();
			double val = 0.0;
			while(intIt.hasNext())
			{
				int frame = intIt.nextInt();
				val += statisticsGenerator.getWordFrameFrequency(frame, word) > 0.0 ? 1.0 : 0.0;
			}
			val = -val/freq*additonalScoreWord(word,statisticsGenerator);
			if(val != 0.0 && this.checkForNonNumber(statisticsGenerator, word))
			{putIntoMap(word, val);}
		}
		return sort();
	}
	/**
	 * this method can be used to scale the word score for the given word by some factor
	 * @param word
	 * @param statisticsGenerator
	 * @return
	 */
	protected double additonalScoreWord(int word,
			StatisticsGenerator statisticsGenerator)
	{return 1.0;}
	/* (non-Javadoc)
	 * @see com.gragra.nounIdentification.Scorer#sortFrames(it.unimi.dsi.fastutil.ints.IntCollection, com.gragra.frameStatistics.StatisticsGenerator, it.unimi.dsi.fastutil.ints.IntSet)
	 */
	@Override
	public IntList sortFrames(IntCollection frames, StatisticsGenerator sg,
			IntSet restriction, int iteration)
	{
		this.sortFrames(frames, sg);
		sorter.clear();
		if(iteration == 0)
		{
			IntIterator iit = frequent.iterator();
			while(iit.hasNext())
			{
				int frame = iit.nextInt();
				double val = intialScore(sg, frame);
				if(val != 0.0)
				{putIntoMap(frame, val);}
			}
		}
		else
		{
			IntIterator iit = frequent.iterator();
			while(iit.hasNext())
			{
				int frame = iit.nextInt();
				if(this.checkForPunct(sg, frame))
				{continue;}
				double freq = sg.getFrameFrequency(frame);
				IntIterator intIt = restriction.iterator();
				double val = 0.0;
				while(intIt.hasNext())
				{
					int word = intIt.nextInt();
					if(this.checkForNonNumber(sg, word))
					{val += sg.getWordFrameFrequency(frame, word);}
				}
				val = -(val/freq)*additionalScoreFrame(frame,sg);
				if(val != 0.0)
				{putIntoMap(frame, val);}
			}
		}
		return sort();
	}
	/**
	 * this method adds the given key to the map used for sorting with the given value
	 * @param key
	 * @param val
	 */
	protected void putIntoMap(int key, double val)
	{
		IntArrayList ial = this.sorter.get(val);
		if(ial == null)
		{
			ial = new IntArrayList();
			this.sorter.put(val, ial);
		}
		ial.add(key);
	}
	/**
	 * this method is used and can be override for giving the initial score for frames in the first iteration
	 * before any word types are assigned to the class that is being investigated
	 * @param sg
	 * @param frame
	 * @return
	 */
	protected double intialScore(StatisticsGenerator sg, int frame)
	{
		if(checkForPunct(sg, frame))
		{return 0.0;}
		double count = 0.0;
		IntIterator iit = sg.getCoOccurringWords(frame).iterator();
		while(iit.hasNext())
		{
			int word = iit.nextInt();
			if(checkForNonNumber(sg,word))
			{count += 1.0;}
		}
		return -count/sg.getFrameFrequency(frame);
	}
	/**
	 * 
	 * @param sg
	 * @param word
	 * @return
	 */
	protected boolean checkForNonNumber(StatisticsGenerator sg, int word)
	{return !StringToInt.reverse(word).matches(".*\\d.*");}
	/**
	 * @param sg
	 * @param frame
	 * @return
	 */
	protected boolean checkForPunct(StatisticsGenerator sg, int frame)
	{
		boolean check = false;
		String one = StringToInt.reverse(sg.getFrameStructure(frame)[0]);
		String two = StringToInt.reverse(sg.getFrameStructure(frame)[2]);
		if(one.matches("(\\p{Punct}+|\\*.*\\*|-.*-)"))
		{check = true;}
		if(two.matches("(\\p{Punct}+|\\*.*\\*|-.*-)"))
		{check = true;}
		return check;
	}
	/**
	 * this method can be used to scale the score of a frame by some factor
	 * @param frame
	 * @param sg
	 * @return
	 */
	protected double additionalScoreFrame(int frame, StatisticsGenerator sg)
	{return 1.0;}
	/**
	 * this list is used as the return for the sorting operation
	 */
	private final IntList il = new IntArrayList();
	/**
	 * used to feed the values from the map into the list
	 * @return
	 */
	private IntList sort()
	{
		il.clear();
		ObjectIterator<IntArrayList> iit = sorter.values().iterator();
		while(iit.hasNext())
		{
			IntArrayList ial = iit.next();
			for(int i=0;i<ial.size();++i)
			{il.add(ial.get(i));}
		}
		return il;
	}
	/**
	 * 
	 * @return
	 */
	public IntArrayList getFrequent()
	{return frequent;}
	/**
	 * the map used for storing and sorting weighting results
	 */
	private final Double2ObjectSortedMap<IntArrayList> sorter = new Double2ObjectRBTreeMap<IntArrayList>();
}